/**
 * 
 */
package uk.ac.ebi.curtain.utils.file.maq;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;

import org.apache.commons.io.IOUtils;

import uk.ac.ebi.curtain.utils.data.IdentifierProcessor;
import uk.ac.ebi.curtain.utils.data.ReadType;
import uk.ac.ebi.curtain.utils.data.impl.MaqIdentifierProcessor;
import uk.ac.ebi.curtain.utils.file.FileInfo;
import uk.ac.ebi.curtain.utils.io.ParseException;
import uk.ac.ebi.curtain.utils.io.impl.FileIO;
import uk.ac.ebi.curtain.utils.match.MatchDetails;
import uk.ac.ebi.curtain.utils.match.MatchEntryMapper;
import uk.ac.ebi.curtain.utils.match.MatchParser;
import uk.ac.ebi.velvet.model.Strand;

/**
 * @author mhaimel
 *
 */
public class MaqMapParser implements MatchParser {
	
	private final IdentifierProcessor proc = new MaqIdentifierProcessor();
	
	@Override
	public void parse(FileInfo info,MatchEntryMapper mapper) throws ParseException{
		BufferedReader in = null;
		try {
			in = new FileIO(info.getFile()).getBufferedReader();
			parse(in,info,mapper);
		} catch (IOException e) {
			throw new ParseException("Problems reading File " + info);
		} finally{
			IOUtils.closeQuietly(in);
		}				
	}

	@Override
	public void parse(BufferedReader in, FileInfo info, MatchEntryMapper mapper) throws IOException {
		String line = null;
		boolean isPaired = info.getReadType().isIn(ReadType.getPaired());
		
		while((line = in.readLine()) != null){
			if(line.length() > 0){
				String[] arr = splitString(line,'\t',14);
				parseEntry(info, mapper, arr, isPaired);
			}
		}
	}

	protected void parseEntry(FileInfo info, MatchEntryMapper mapper, String[] arr, boolean isPaired) {
		String readId = parseReadId(arr,isPaired);
		String refId = parseReferenceId(arr);		
		int mqual = parseMappingQuality(arr);		
		int semQual = parseSemQuality(arr);		
		int altQual = parseAltQuality(arr);
		MatchDetails details = new MatchDetails(info, refId, readId, new int[]{ altQual,mqual,semQual});
		
//		MapDetails det = new MapDetails();
		MaqFlag flag = parseFlag(arr);
		details.setReferenceLocation(parseLocation(arr));
		details.setAlignOrientation(parseStrand(arr));
		details.setReadOrientation(
				parseReadOrientation(arr,isPaired));
		details.setReadLength(parseLength(arr));
		
		// assume full length alignment for maq 
		details.setAlignLength(details.getReadLength());

		details.setMapped(true);
		details.setAlignBlocks(1);
		if(!flag.equals(MaqFlag.NotPaired)){
			details.setPaired(true);
			if(flag.equals(MaqFlag.ChromosomeDiff)){
				details.setDifferentContig(true);
			}	
			details.setOtherMapped(true);	
			if(flag.equals(MaqFlag.PairedSingleMapped)){
				details.setOtherMapped(false);
			} else if (flag.equals(MaqFlag.PairedMatedMapped)){
				details.setMapped(false);		
				details.setAlignBlocks(0);
			}
			if(flag.equals(MaqFlag.PairedCorrect) || flag.equals(MaqFlag.PairedCorrect2)){
				details.setProperPaired(true);
			}
		}
		
		mapEntry(mapper,details);
	}

	private int parseLength(String[] arr) {
		return Integer.valueOf(arr[13]);
	}

	private Strand parseReadOrientation(String [] arr, boolean isPaired) {
		Strand strand = Strand.UNKOWN;
		if(isPaired){
			String readId = getBaseReadId(arr);
			strand = proc.getStrand(readId);
		}
		return strand;
	}


	protected String parseReadId(String[] arr, boolean isPaired) {
		String readId = getBaseReadId(arr);
		if(isPaired){
			readId = proc.removeStrandInformation(readId);
		}
		readId = proc.cleanId(readId);
		if(readId.endsWith(".") || readId.endsWith("/")){
			readId = readId.substring(0, readId.length()-1);
		}
		return readId;
	}

	/**
	 * Unmodified Read id (with possible extension)
	 * @param arr
	 * @return
	 */
	protected String getBaseReadId(String[] arr){
		return arr[0];
	}
	
	protected void mapEntry(MatchEntryMapper mapper, MatchDetails details) {
		mapper.mapEntry(details);
	}

	protected Integer parseMappingQuality(String[] arr) {
		return Integer.valueOf(arr[6]);
	}
	protected Integer parseSemQuality(String[] arr) {
		return Integer.valueOf(arr[7]);
	}
	protected Integer parseAltQuality(String[] arr) {
		return Integer.valueOf(arr[8]);
	}

	protected Strand parseStrand(String[] arr) {
		if(arr[3].length() != 1){
			throw new StringIndexOutOfBoundsException("Invalid Strand information! " + arr[3]);
		}
		return Strand.getStrand(arr[3].charAt(0));
	}

	protected Integer parseLocation(String[] arr) {
		return Integer.valueOf(arr[2]);
	}

	protected String parseReferenceId(String[] arr) {
		return new String(arr[1].toCharArray());
	}

	protected MaqFlag parseFlag(String[] arr) {
		return MaqFlag.getFlag(Integer.valueOf(arr[5]));
	}
	
	protected String[] splitString(String line, char c, int limit) throws IOException {
		ArrayList<String> arr = new ArrayList<String>();
		int prevPos = 0;
		int currPos = 0;
		
		while((currPos = line.indexOf(c, prevPos)) > 0){
			arr.add(line.substring(prevPos, currPos));
			currPos += 1; // jump char separator
			prevPos = currPos;
		}
		if(prevPos < line.length()){
			arr.add(line.substring(prevPos, line.length()));
		}
		if(arr.size() < limit){
			throw new IOException("Field limit not reached: "+arr.size() + " < " + limit + "! >" + line + "<");
		}
		return arr.toArray(new String[0]);
	}

	

}
